#define CONFIG_FILE	"dnfs-server.conf"
#define NODEIP		"nodeip"
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <math.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <arpa/inet.h>
#include "dnfs.h"
#include "common.h"
#include "server.h"

#define OPEN_MAX	1024*1024
struct nodelist *nodehead;
struct filelist *filehead;
extern unsigned int TS;
extern uint64_t LS; //Low space
extern char METAROOT[NAMELEN];

int init()
{
	FILE *fp;
	char type[LINELEN];
	char value[LINELEN];

	if ((fp = fopen(CONFIG_FILE, "r")) == NULL) {
		printf("can't open %s\n", CONFIG_FILE);
		return -1;
	}
	while (1) {
		if (fscanf(fp, "%s%s", type, value) != 2) {
			PDEBUG("init finish\n");
			break;
		}	
//		PDEBUG("%s %s\n", type, value);
		if (strcmp(type, "TS") == 0) {
			char unit = value[strlen(value) - 1];
			if (unit == 'M' || unit == 'm') {//单位为m
				value[strlen(value) - 1] = 0;
				TS = atoi(value) * 1024 * 1024;
			}
			else if (unit == 'K' || unit == 'k') {
				value[strlen(value) - 1] = 0;
				TS = atoi(value) * 1024;
			}
			else{//单位为Byte
				TS = atoi(value);	
			}	
			PDEBUG("TS = %dBytes\n", TS);
		}
		else if (strcmp(type, "LS") == 0) {//以MByte为单位
			char unit = value[strlen(value) - 1];
			if (unit == 'G' || unit == 'g') {//单位为G
				value[strlen(value) - 1] = 0;
				LS = atoi(value) * 1024;
			}
			else if (unit == 'M' || unit == 'm') {//单位为m
				value[strlen(value) - 1] = 0;
				LS = atoi(value);
			}
			else if (unit == 'K' || unit == 'k') {
				value[strlen(value) - 1] = 0;
				LS = atoi(value) * 1024;
			}
			else{//默认单位为GByte
				LS = atoi(value) * 1024;	
			}	
			PDEBUG("LS = %ld\n", LS);
		}
		else if (strcmp(type, "METAROOT") == 0) {
			strcpy(METAROOT, value);
			PDEBUG("METAROOT = %s\n",  METAROOT);
		}
	}

	struct rlimit r;
    	int resource=RLIMIT_NOFILE;
    	PDEBUG("the original OPEN_MAX=%ld,",sysconf(_SC_OPEN_MAX));
    	r.rlim_cur=OPEN_MAX;
    	r.rlim_max=OPEN_MAX;
    	if(setrlimit(resource,&r)!=0) {
    		PDEBUG("Set OPEN_MAX failed.Make sure you are ROOT!\n");
        	return -1;
    	}
	PDEBUG("set it to=%ld %ld\n",r.rlim_cur,r.rlim_max);
	return 0;
}

//struct nodelist* find_node(char *ip)
struct nodelist* find_node(IP ip)
{
	struct nodelist* p = nodehead;
	while(p != NULL && ip != p->ip)
		p = p->next;
	return p;
}

int insert_osd(os_loadreport_in *arg)
{
//	PDEBUG("[insert_osd] %u\n", arg->ipn);
	struct nodelist *osd = (struct nodelist *) malloc (sizeof(struct nodelist));
	if (osd == NULL) {
		printf("malloc failed\n");
		exit(-2);
	}
	osd->ip = arg->ipn;
	osd->clo = NULL;
	osd->avail_space = arg->avail_space;
	osd->load = arg->net_recv + arg->net_send + arg->disk_read + arg->disk_write;
	osd->next = osd->pre = NULL;
	if (nodehead == NULL) {//the list is null
		nodehead = osd;
		return 0;
	}
	struct nodelist *p = nodehead;
	while (p->load < osd->load && p->next != NULL) {
		p = p->next;
	}
	if (p->next == NULL) {//insert as the last
		p->next = osd;
		osd->pre = p;
	}
	else if (p == nodehead) {//insert as the nodehead
		osd->next = p;
		p->pre = osd;
		nodehead = p;
	}
	else {//in the middle
		p->pre->next = osd;
		osd->pre = p->pre;
		osd->next = p;
		p->pre = osd;
	}
//PDEBUG("[insert_osd]succeed\n");
	return 0;
}

int update_osd(struct nodelist *osd, os_loadreport_in *arg)
{
	//PDEBUG("[update_osd]\n");
	osd->load = arg->net_recv + arg->net_send + arg->disk_read + arg->disk_write;
	osd->avail_space = arg->avail_space;
	OSD *p;
	if (osd->next != NULL && osd->load >= osd->next->load) {
		p=osd->next;
		while (osd->load >= p->load && p->next!=NULL) {
		//	PDEBUG("%d,%d\n",osd->load,p->load);
			p=p->next;
		}
		//delete the node from the original place first
		if (osd->pre!=NULL) {
			osd->pre->next=osd->next;
			osd->next->pre=osd->pre;
		}
		else{
			osd->next->pre=NULL;
			nodehead=osd->next;
		}
		
		//insert the node to the new place
		if(p->load > osd->load) {
			p->pre->next=osd;
			osd->pre=p->pre;
			osd->next=p;
			p->pre=osd;
		}
		else {//p->next==NULL ,that means "osd" should append to the end
			p->next=osd;
			osd->pre=p;
			osd->next=NULL;
		}
		return 0;
	}	
	//the node's busy value has been decreased
	if(osd->pre!=NULL && osd->load < osd->pre->load) {
		p=osd->pre;
		while( osd->load < p->load && p->pre!=NULL ) {
			p=p->pre;
		}		
		
		if(osd->next!=NULL) {
			osd->pre->next=osd->next;
			osd->next->pre=osd->pre;
		}
		else{
			osd->pre->next=NULL;
		}
		//if(p->pre!=NULL) {
		if(p->load <= osd->load) {
			p->next->pre=osd;
			osd->next=p->next;
			osd->pre=p;
			p->next=osd;
		}		
		else{//p->pre==NULL ,that means "osd" should add to the head	
			p->pre=osd;
			osd->next=p;
			osd->pre=NULL;
			nodehead=osd;
		}
	}
	return 0;
}

/*
 * 重建并返回地址为ipp的osd的CLIENT结构。
 */
CLIENT * recreate_clo(IP ipn) 
{
	PDEBUG("[recreate_clo]: %u\n", ipn);
	char ipp[INET_ADDRSTRLEN];
	if (inet_ntop(AF_INET, &ipn, ipp, INET_ADDRSTRLEN) == NULL) {
		printf("inet_ntop failed\n");
		return NULL;
	}
	CLIENT *clo = NULL;
	clo = clnt_create (ipp, SO, SO_VERS, "tcp");
	if (clo == NULL) {
		clnt_pcreateerror (ipp);
	}
//	printf("out recreate_clo\n");
	return clo;	
}


int print_nodelist()
{
	struct nodelist* np = nodehead;
	//output all the node's ip
	PDEBUG("*****print all the node*****\n");
	while (np != NULL) {
		PDEBUG("[%u] %ld, ", np->ip, np->load);
//		fprintf(logfile,"%s %d\n",np->ip,np->load);
		np = np->next;
	}
	PDEBUG("\n****************************\n");
//	fprintf(logfile,"\n****************************\n");
	return 0;
}

//用sha得到ossname的密钥，并把密钥转换成字符串格式
//@ossname是传递过来的变量
//@oid是要返回的结果
/*
int getoid(const char * ossname, char *oid) {
	unsigned char md[20];
	unsigned char *c;
//	PDEBUG("[getoid]:%s, len = %d\n", ossname, strlen(ossname));
	SHA1(ossname, strlen(ossname), md);
	c = md;
	//	c2 = res->oid[0];
//	int md_len = strlen(md);
	int md_len = 20;
	int oid_index = 0;
	while( c < md_len + md) {
		oid_index += sprintf(oid + oid_index, "%X", *c);
//		PDEBUG("%X-", *c);
		c++;
	}
//	PDEBUG("\n");
//	PDEBUG("\noid:%s\n", oid);
	return 0;
}
*/

/*
 * 由path得到文件在MDS中的inum，然后"inum + -pid" = oid
 * @filename是文件的名称
 * @pid是分条在文件中的块号
 * @oid是得到并返回的分条号（字符串形式）
 * @成功返回0；失败返回-1.
 */
int getoid(const char *path, int pid, char *oid)
{
	struct stat stbuf;
	if (lstat(path, &stbuf) == -1) {
		printf("lstat failed: %s\n", strerror(errno));
		return -1;
	}
	sprintf(oid, "%lx-%d", stbuf.st_ino, pid);
	return 0;
}


int get_client_path(const char *src, char *dst)
{
	unsigned int len = strlen(METAROOT);
	strcpy(dst, src + len);
	PDEBUG("[get_client_path]: %s -> %s\n", src, dst);

	return 0;
}

void print_dstat(struct DSTAT *stbuf, const char *filename)
{
#if __WORDSIZE == 64
	PDEBUG("<print_DSTAT>\"%s\" : %ld %ld %o %ld %d %d %ld %ld %ld %ld %ld %ld %ld\n", 
	filename, stbuf->st_dev, stbuf->st_ino, stbuf->st_mode, stbuf->st_nlink, 
	stbuf->st_uid, stbuf->st_gid, stbuf->st_rdev, stbuf->st_size, stbuf->st_blksize,
	stbuf->st_blocks, stbuf->atime, stbuf->mtime, stbuf->ctime);
#else
	PDEBUG("<print_DSTAT>\"%s\" : %lld %lld %o %lld %d %d %lld %lld %lld %lld %lld %lld %lld\n", 
	filename, stbuf->st_dev, stbuf->st_ino, stbuf->st_mode, stbuf->st_nlink, 
	stbuf->st_uid, stbuf->st_gid, stbuf->st_rdev, stbuf->st_size, stbuf->st_blksize,
	stbuf->st_blocks, stbuf->atime, stbuf->mtime, stbuf->ctime);
#endif

}

void print_stat(struct stat *stbuf, const char *filename)
{
	PDEBUG("<print_stat>\"%s\" : %ld %ld %o %ld %d %d %ld %ld %ld %ld %ld %ld %ld\n",
	filename, stbuf->st_dev, stbuf->st_ino, stbuf->st_mode, stbuf->st_nlink, 
	stbuf->st_uid, stbuf->st_gid, stbuf->st_rdev, stbuf->st_size, stbuf->st_blksize,
	stbuf->st_blocks, stbuf->st_atime, stbuf->st_mtime, stbuf->st_ctime);
}

void print_meta(const char *path)
{
	FILE *fp;
	struct DSTAT stbuf;
	CHUNKNUM chunknum;
//	OID oid = (OID) malloc (OIDLEN);
//	IP ip;// = (IP) malloc (IPLEN);
//	CHUNKSIZE chunksize;

	if ((fp = fopen(path, "r")) == NULL) {
		printf("open %s faild: %s\n", path, strerror(errno));
		return ;
	}
	PDEBUG("/***************************************/\n");
	Fread((char *)&stbuf, sizeof(stbuf), 1, fp);
	print_dstat(&stbuf, path);
//PDEBUG("pos4 = %ld\n", ftell(fp));
	Fread((char *)&chunknum, sizeof(chunknum), 1, fp);
	PDEBUG("chunknum = %d\n", chunknum);

	int i;
	CHUNK_MEM chunk;
	for (i = 0; i < chunknum; i++) {	/////test here
		if (Fread(&chunk, ICHUNK_SIZE, 1, fp) < 0 ) {
			printf("fread chunk failed\n");
		}
		PDEBUG("%d: oid = %s, ip = %u, psize = %d\n", i, chunk.oid, chunk.ip, chunk.size);

	}
	
	fclose(fp);
//	free(oid);
//	free(ip);
	PDEBUG("/***************************************/\n");
}


//0或出错返回负数
int Fread(void *buf, size_t size, size_t nmemb, FILE *fp)
{
	int res;
	res = fread((char *)buf, size, nmemb, fp);
	if (res < nmemb) {
		res = -errno;
		printf("fread failed: %s\n", strerror(errno));
	}
	else 
		res = 0;
		
	return res;
}

//0或出错返回负数
int Fwrite(const void *buf, size_t size, size_t nmemb, FILE *fp)
{
	int res;
	res = fwrite((const char *)buf, size, nmemb, fp);
	if (res < nmemb) {
		res = -errno;
		printf("fwrite failed: %s\n", strerror(errno));
	}
	else 
		res = 0;
		
	return res;
}

//0或-errno
int Fseek(FILE *fp, long offset, int whence)
{
	int res;
	res = fseek(fp, offset, whence);
	if (res != 0) {
		res = -errno;
		printf("fseek failed: %s\n", strerror(errno));
	}
	return res;
}

/*
 * 用于获取offset处的所处的chunk的序号（从0开始）
 */
int getcid(uint64_t offset)
{
	if (offset < 0) //这时
		offset = 0;
	int x = ceil(offset / TS);
//	if (offset % TS == 0 && x != 0)
//		x--;
	return x;
}

/*
 * 更新文件的newsize，chunknum，以及文件的所有chunk信息
 */
int update_meta(const char *path, uint64_t newsize, const void *buf, uint32_t len)
{
	FILE *fp = NULL;
	DSTAT stbuf;
	fp = fopen(path, "r+");
	if (fp == NULL) {
		printf("open %s failed: %s\n", path, strerror(errno));
		return -1;
	}
	int n_chunknum = getcid(newsize - 1) + 1;
	Fseek(fp, sizeof(stbuf), SEEK_SET);
	Fwrite(&n_chunknum, sizeof(n_chunknum), 1, fp);
	PDEBUG("buf_len = %u\n", len);
	CHUNK_MEM* tmp = (CHUNK_MEM*)buf;
	int i;
	for(i = 0; i < n_chunknum; i++) {
		PDEBUG("%s\t%u\t%u\n", tmp->oid, tmp->ip, tmp->size);
		tmp ++;
	}

	Fwrite(buf, len, 1, fp);
	return 0;
}

int insert_file(FILELIST *file)
{
	if (filehead == NULL) {
		filehead = file;
		return 0;
	}
	file->next = filehead;
	filehead->pre = file;
	filehead = file;
print_filelist();
	return 0;	
}

FILELIST * find_file(const char *path)
{
	FILELIST *p = filehead;
	while (p != NULL && strcmp(p->path, path) != 0)
		p = p->next;
	return p;
}

int delete_file(FILELIST *file)
{
	if (file == filehead) {//the first one in the file list
		filehead = filehead->next;
	//	free(file);
	}
	else if (file->next == NULL) {//the last one
		file->pre->next = NULL;
	//	free(file);
	}
	else {
		file->pre->next = file->next;
		file->next->pre = file->pre;
	}
	free(file);
print_filelist();
	return 0;
}

void print_file(FILELIST *file)
{
	PDEBUG("\t[print_file]%s\n", file->path);
	PDEBUG("\t\t[w]%u %u\n", file->wcount, file->wip);
	PDEBUG("\t\t[r]%u ", file->rcount);
	CLIPER *p = file->rlist;
	while (p != 0) {
		PDEBUG("%u ", p->ip);
		p = p->next;
	}
	printf("\n");
}

void print_filelist()
{
	PDEBUG("[print_filelist]\n");
	FILELIST *p = filehead;
	while(p != NULL) {
		print_file(p);
		p = p->next;
	}
}

int insert_client(struct client **head, struct client *p)
{
	if (*head == NULL) {
		*head = p;
		return 0;
	}
	p->next = *head;
	(*head)->pre = p;
	(*head) = p;
	return 0;	
}

CLIPER* find_client(CLIPER *head, IP ip)
{
	CLIPER *p = head;
	while (p != NULL && p->ip != ip)
		p = p->next;
	return p;
}

int delete_client(CLIPER **head, CLIPER *p)
{
	if (p->pre == NULL) {//the first one in the file list
		*head = p->next;
	//	free(file);
	}
	else if (p->next == NULL) {//the last one
		p->pre->next = NULL;
	//	free(file);
	}
	else {
		p->pre->next = p->next;
		p->next->pre = p->pre;
	}
	free(p);
	return 0;
}

